package ball;

import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Iterator;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;


public class BilliardBall extends JFrame {

	/**
	 */
	private static final long serialVersionUID = 1L;
	public static final int GAME_WIDTH  = 800;
	public static final int GAME_HEIGHT = 600;

	public Image offScreenImage = null;
	public Graphics bg = null;
	public TablePanel pTable = null;
	public ProcessPanel pInfo = null;

	public BilliardBall() {
		pInfo = new ProcessPanel();
		pTable = new TablePanel(this);  // 暂时, 就这么设为.
	}
	
	public void launchFrame() {
		setTitle("台球游戏");
		setSize(GAME_WIDTH, GAME_HEIGHT);
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		setLayout(null);
		pTable.setBounds(100, 50, pTable.TABLE_WIDTH, pTable.TABLE_HEIGHT);
		add(pTable);
		pInfo.setBounds(100, 400, pInfo.PROCESS_WIDTH, pInfo.PROCESS_HEIGHT);
		add(pInfo);
		pInfo.addStartActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				pTable.gameStart();
			}
		});
		setResizable(false);
		setVisible(true);
		new PaintThread().start();
	}

	public void update(Graphics g) {
		if ( offScreenImage == null ) {
			offScreenImage = createImage(GAME_WIDTH, GAME_HEIGHT);
		}
		bg = offScreenImage.getGraphics();
		Color c = bg.getColor();
		bg.setColor(Color.WHITE);
		bg.fillRect(0, 0, GAME_WIDTH - 1, GAME_HEIGHT - 1);
		paint(bg);
		bg.setColor(c);	
		g.drawImage(offScreenImage, 0, 0, null);
	}

	public void paint(Graphics g) {
		super.paint(g);
		Color c = g.getColor();
		// add code here: to draw many ball with movement;
		g.setColor(c);
	}

	public int getPower() {
		return pInfo.getPower();
	}

	//change
	public void initialPower() {
		pInfo.initialPower();
	}

	public void updatePower() {
		pInfo.updatePower();	
	}

	public void addScore() {
		pInfo.addScore();
	}

	public void initScore() {
		pInfo.initScore();
	}

	public static void main(String[] args) {
		new BilliardBall().launchFrame();	
	}
	
	private class PaintThread extends Thread {
		public static final int INTERVAL = 80;
		public void run() {
			while ( true ) {
				try {
					Thread.sleep(INTERVAL); //
					pTable.move();
					update(getGraphics());  // 当然 repaint() 这个函数也是行的.
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}	

}

// 就一个球而言, 其应该储存什么信息?
class Ball {  // 封装信息: ball之间.

	public static final double EPS = 0.01;
	public static final int    SLICE = 45;
	public static final int    OFFSET = 16;
	public static final double PROBABILITY = 2.0;

	public Color color = null;
	public double x, y, radian;
	public double vx, vy; // 这里的vx, vy 表示运动矢量.
	public BufferedImage picture = null;
	public boolean live = false;

	public Ball() {
	//	x = y = 50;	
	//	vx = vy = 0;
		live = true;
		setData(150, 150, 0, 0);
		radian = 10;
		color = Color.WHITE;
		picture = ImageLoader.getIndexOf(0);
	}	

	public Ball(double x, double y, Color color) {
		live = true;
		setData(x, y, 0, 0);
		radian = 10;
		this.color = color;
		if ( color != Color.WHITE ) {
			picture = ImageLoader.getIndexOf(1);
		}
	}
	
	public void draw(Graphics g) {
		if ( isLive() == true ) {
			Color c = g.getColor();
			g.setColor(color);
		//g.fillOval((int)(x - radian + 0.5), (int)(y - radian + 0.5), (int)(radian * 2 + 0.5), (int)(radian * 2 + 0.5));		
			g.drawImage(picture, (int)(x - radian + 0.5), (int)(y - radian + 0.5), null);
			g.setColor(c);
		}
	}

	public boolean isHited(Point p) {  // 这个是正方形包含, 
	//	if ( p.x == x && p.y == y ) return false;
		return (p.x >= x - radian && p.x <= x + radian && p.y >= y - radian && p.y <= y + radian); 
	}

	public void setData(double x, double y, double vx, double vy) {
		this.x = x;
		this.y = y;
		this.vx = vx;
		this.vy = vy;
	}	

	public void setData(int value, Point p) {
		
		double spring = Math.sqrt((p.x - x) * (p.x - x) + (p.y - y) * (p.y - y));
	//	System.out.println("Spring:" + spring);
		vx = (p.x - x) / spring * value * 25;
		vy = (p.y - y) / spring * value * 25;
	//	System.out.println("vx:" + vx + " vy: " + vy);

	}

	public void setProperty(double x, double y, Color c) {
		this.x = x;
		this.y = y;
		this.color = c;
		vx = vy = 0;
		if  ( color != Color.WHITE )
			picture = ImageLoader.getIndexOf(1);
		else
			picture = ImageLoader.getIndexOf(0);
	}
	// 恐怕 这里的速度需要测试出来.
	public void forward() {

	}

	public boolean isStayed() {
		if ( Math.abs(vx) <= EPS && Math.abs(vy) <= EPS ) {
			vx = vy = 0;
			return true;
		}
		return false;
	}

	public boolean isTouched(double x1, double y1, double x2, double y2) {
		double dist = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
		return dist < 2 * radian;
	}
	
	public boolean isLive() {
		return live;
	}

	public void setLive(boolean live) {
		this.live = live;
	}
 
	public int collides(Ball s) {
		int step = 1;
		double px, py, nx, ny;
		double tx1, ty1, cx1, cy1;
		double tx2, ty2, cx2, cy2;

		if ( isStayed() == true && s.isStayed() == true ) 
			return SLICE;
		if ( isStayed() == true ) {
			px = py = 0;
		} else  {
			px = vx / Math.sqrt(vx * vx + vy * vy);
			py = vy / Math.sqrt(vx * vx + vy * vy);
		}
		if ( s.isStayed() == true ) {
			nx = 0; ny = 0;
		} else {
		       	nx = s.vx / Math.sqrt(s.vx * s.vx + s.vy * s.vy);
		       	ny = s.vy / Math.sqrt(s.vx * s.vx + s.vy * s.vy);
		}
//tx = mBall.vx - (mBall.vx * (mstep - step) / spring);
		while ( step < SLICE ) { 	
			tx1 = vx - step * px;
			ty1 = vy - step * py;
			if ( (tx1 > 0 && vx < 0) || (tx1 < 0 && vx > 0) ) tx1 = 0.0;
			if ( (ty1 > 0 && vy < 0) || (ty1 < 0 && vy > 0) ) ty1 = 0.0;
			cx1 = x + step * (vx + tx1) / 2000.0;
			cy1 = y + step * (vy + ty1) / 2000.0;
			
			tx2 = s.vx - step * nx;
			ty2 = s.vy - step * ny;
			if ( (tx2 > 0 && vx < 0) || (tx2 < 0 && vx > 0) ) tx2 = 0.0;
			if ( (ty2 > 0 && vy < 0) || (ty2 < 0 && vy > 0) ) ty2 = 0.0;
			cx2 = s.x + step * (s.vx + tx2) / 2000.0;
			cy2 = s.y + step * (s.vy + ty2) / 2000.0;
			if ( isTouched(cx1, cy1, cx2, cy2) == true ) break; // 表接触
			step++;
		}
		return step;
	}
	
	public int collidesWall(int width, int height) {
		int step = 1;
		double px = 0, py = 0, tx = 0, ty = 0, cx = 0, cy = 0;
		if ( isStayed() == true ) return SLICE;
		px = vx / Math.sqrt(vx * vx + vy * vy);
		py = vy / Math.sqrt(vx * vx + vy * vy);
		while ( step < SLICE ) {
			tx = vx - step * px;
			ty = vy - step * py;
			if ( (tx > 0 && vx < 0) || (tx < 0 && vx > 0) ) tx = 0.0;
			if ( (ty > 0 && vy < 0) || (ty < 0 && vy > 0) ) ty = 0.0;
			cx = x + step * (vx + tx) / 2000.0;
			cy = y + step * (vy + ty) / 2000.0;
			if ( cx - radian <= 0 + OFFSET || cx + radian >= width - OFFSET 
					|| cy - radian <= 0 + OFFSET || cy + radian >= height - OFFSET ) {
			       
				if ( isSpecial(cx, cy, width, height) == false ) break; 
			}
			step++;
		}	
		return step;
	}

	public boolean isSpecial(double cx, double cy, int width, int height) {  // 增强真实感,(1) 进洞.
		
		double r1 = OFFSET, r2 = (width - 2 * OFFSET) / 2;
		for ( int i = 0; i < 3; i++ ) {
			if ( Math.sqrt((cx - r2 * i - OFFSET) * (cx - r2 * i - OFFSET) + (cy - r1) * (cy - r1)) < radian * PROBABILITY )
				return true;
		}
		r1 = height - OFFSET;
		for ( int i = 0; i < 3; i++ ) {
			if ( Math.sqrt((cx - r2 * i - OFFSET) * (cx - r2 * i - OFFSET) + (cy - r1) * (cy - r1)) < radian * PROBABILITY )
				return true;
		}
		return false;

	}

	public int collidesHole(int width, int height) {
		int step = 1;
		double px = 0, py = 0;
	       	double tx = 0, ty = 0;
		double cx = 0, cy = 0;
		double r1 = 0, r2 = 0;
		if ( isStayed() == true ) return SLICE;
		px = vx / Math.sqrt(vx * vx + vy * vy);
		py = vy / Math.sqrt(vx * vx + vy * vy);	
		while ( step < SLICE ) {
			tx = vx - step * px;
			ty = vy - step * py;
			if ( (tx > 0 && vx < 0) || (tx < 0 && vx > 0) ) tx = 0.0;
			if ( (ty > 0 && vy < 0) || (ty < 0 && vy > 0) ) ty = 0.0;
			cx = x + step * (vx + tx) / 2000.0;
			cy = y + step * (vy + ty) / 2000.0;
			r1 = OFFSET; r2 = (width - 2 * OFFSET) / 2;
			for ( int i = 0; i < 3; i++ ) {
			   //    	double tvalue = Math.sqrt((cx - r2 * i) * (cx - r2 * i) + (cy - r1) * (cy - r1));	
			//	System.out.println("tvalue :" + tvalue + ":radian2" + (radian * 2));
				if ( Math.sqrt((cx - r2 * i - OFFSET) * (cx - r2 * i - OFFSET) + (cy - r1) * (cy - r1)) < radian * PROBABILITY )
					return step;
			}
			r1 = height - OFFSET;
			for ( int i = 0; i < 3; i++ ) {
			 //      	double tvalue = Math.sqrt((cx - r2 * i) * (cx - r2 * i) + (cy - r1) * (cy - r1));	
			//	System.out.println("tvalue :" + tvalue + ":radian2" + (radian * 2));
				if ( Math.sqrt((cx - r2 * i - OFFSET) * (cx - r2 * i - OFFSET) + (cy - r1) * (cy - r1)) < radian * PROBABILITY )
					return step;
			}
			step++;
		}
		return step;	

	}

	public void update(int step) {
		double px = 0, py = 0;
		double tx, ty, cx, cy;
		if ( isStayed() == true ) {
			px = py = 0;
		} else  {
			px = vx / Math.sqrt(vx * vx + vy * vy);
			py = vy / Math.sqrt(vx * vx + vy * vy);
		}
		tx = vx - step * px;
		ty = vy - step * py;
		if ( (tx > 0 && vx < 0) || (tx < 0 && vx > 0) ) tx = 0.0;
		if ( (ty > 0 && vy < 0) || (ty < 0 && vy > 0) ) ty = 0.0;
		cx = x + step * (vx + tx) / 2000.0;
		cy = y + step * (vy + ty) / 2000.0;
		setData(cx, cy, tx, ty);
	}

	public void changes(Ball s) {  // 这个函数, 则是碰撞反应的核心, 最重要的要防止粘滞住...
		
		double px, py;
		double ux1, uy1, ux2, uy2;
		double ux3, uy3, ux4, uy4;
		px = (s.x - x) / Math.sqrt((s.x - x) * (s.x - x) + (s.y - y) * (s.y - y));  // 这边是单位化
		py = (s.y - y) / Math.sqrt((s.x - x) * (s.x - x) + (s.y - y) * (s.y - y));

		ux1 = (px * vx + py * vy) * px;
		uy1 = (px * vx + py * vy) * py;
		ux2 = vx - ux1;
		uy2 = vy - uy1;
	
		ux3 = px * (px * s.vx + py * s.vy);
		uy3 = py * (py * s.vx + py * s.vy);
		ux4 = s.vx - ux3;
		uy4 = s.vy - uy3;

		vx = ux2 + ux3;
		vy = uy2 + uy3;

		s.vx = ux1 + ux4;
		s.vy = uy1 + uy4;
		
		// 还要恰当改变两球之间的距离.
		int step = 1;
		while ( true ) {
			x = x + step * vx / 1000.0;
			y = y + step * vy / 1000.0;
			s.x = s.x + step * s.vx / 1000.0;
			s.y = s.y + step * s.vy / 1000.0;
			if ( isTouched(x, y, s.x, s.y) == false ) break;
			if ( isStayed() == true && s.isStayed() == true && isTouched(x, y, s.x, s.y) == true ) { // 潜在的一个死循环.
				double p1 = (x - s.x) / Math.sqrt((s.x - x) * (s.x - x) + (s.y - y) * (s.y - y));
				double p2 = (y - s.y) / Math.sqrt((s.x - x) * (s.x - x) + (s.y - y) * (s.y - y));
				double p3 = (s.x - x) / Math.sqrt((s.x - x) * (s.x - x) + (s.y - y) * (s.y - y));
				double p4 = (s.y - y) / Math.sqrt((s.x - x) * (s.x - x) + (s.y - y) * (s.y - y));
				step = 1;
			//	System.out.println("I frame myself");
				while ( true ) {
					x = x + step * p1 / 200;
					y = y + step * p2 / 200;
					s.x = s.x + step * p3 / 200;
					s.y = s.y + step * p4 / 200;
					if ( isTouched(x, y, s.x, s.y) == false ) return;
					step++;
				}
			} 
			step++;
		}	


	}

	public void changesWall(int width, int height) {
		
		if ( x - radian <= 0 + OFFSET ) {
			x = radian + OFFSET;
			vx = -vx;
		} else if ( x + radian >= width - OFFSET ) {
			x = width - radian - OFFSET;
			vx = -vx;
		} 
		if ( y - radian <= 0 + OFFSET ) {
			y = radian + OFFSET;
			vy = -vy;
		} else if ( y + radian >= height - radian - OFFSET ) {
			y = height - radian - OFFSET;
			vy = -vy;
		}	

	}

	public Point changesHole(int width, int height) {  // 制作消失效果.
		
		double r1, r2;	
		setLive(false);	
		r1 = OFFSET; r2 = (width - 2 * OFFSET) / 2;
		for ( int i = 0; i < 3; i++ ) {
			if ( Math.sqrt((x - r2 * i - OFFSET) * (x - r2 * i - OFFSET) + (y - r1) * (y - r1)) < radian * PROBABILITY )
				return new Point((int)(r2 * i + OFFSET), (int)(r1));
		}
		r1 = height - OFFSET;
		for ( int i = 0; i < 3; i++ ) {
			if ( Math.sqrt((x - r2 * i - OFFSET) * (x - r2 * i - OFFSET) + (y - r1) * (y - r1)) < radian * PROBABILITY )
				return new Point((int)(r2 * i + OFFSET), (int)(r1));
		}
		return new Point(-100, -100);  // 若程序执行到这里,表明程序执行错误.

	}

	public String toString() {  // 这个toString() 用于调试用的.
		return "x: " + x + " y: " + y + " vx " + vx +  " vy " + vy;
	}

//	public boolean is
	//public synchronized ()
	// 当然也可以重载.
	// return .    Math.sqrt((p.x - x) * (p.x - x) + (p.y - y) * (p.y - y)) <= radian ;
	// return    (p.x - x) * (p.x - x) + (p.y - y) * (p.y - y) <= radian *radian;


}


class TablePanel extends JPanel {

	public static final int TABLE_WIDTH  = 600;
	public static final int TABLE_HEIGHT = 300;
	public static final int SLICE = 45;
	
	public static final int CWNULL = 0;   // 表没有碰撞的情况
	public static final int CWBALL = 1;   // 与球相碰
	public static final int CWWALL = 2;   // 与wall相碰
	public static final int CWHOLE = 3;   // 落入hole中 	 

//	public MouseMonitor mouseManager = null;
	public Point pTarget;
	public Ball  mBall;
	public boolean state = false;
	public boolean canFire = false;
	public boolean canPlace = true;
	public ArrayList<Ball> balls = null;
	public ArrayList<Tnode> flashs = null;
	public BufferedImage table = null;
	public BilliardBall parent = null;

	public TablePanel(BilliardBall parent) {
		this.parent = parent;
		table = ImageLoader.getBackground();
		balls = new ArrayList<Ball>();
		flashs = new ArrayList<Tnode>();
		initial();
		gameStart();
	//	balls.add(new Ball());  // 对Ball的构造函数进行修改.
		setSize(TABLE_WIDTH, TABLE_HEIGHT);
		pTarget = new Point(51, 51);  // 先设为一个初始值.
		setCanFire(true);  // 设置fire 为true, 表示能发射
		addMouseMotionListener(new MouseMotionMonitor());
		addMouseListener(new MouseMonitor());
	}

	public void paint(Graphics g) {
		Color c = g.getColor();
		g.setColor(Color.GREEN);
		g.fillRect(0, 0, getSize().width - 1, getSize().height - 1);
		g.drawImage(table, 0, 0, null);
	//	mBall.draw(g);
		for ( int i = 0; i < balls.size(); i++ ) {
			balls.get(i).draw(g);
		}
		if ( isCanFire() == true && isCanPlace() == false ) {
			g.setColor(Color.PINK);
			g.drawLine((int)(mBall.x + 0.5), (int)(mBall.y + 0.5), pTarget.x, pTarget.y);
			g.fillOval(pTarget.x - 5, pTarget.y - 5, 10, 10);
		//	g.drawLine((int)(mBall.x + 1.5), (int)(mBall.y + 1.5), pTarget.x + 1, pTarget.y + 1);
		}	
		synchronized(this) {
			for ( int i = 0; i < flashs.size(); i++ ) {
				flashs.get(i).draw(g);
			}
			Iterator<Tnode> iter = flashs.iterator();
			while ( iter.hasNext() ) {
				Tnode item = iter.next();
				if ( item.isLive() == false )
					iter.remove();
			}
		}		

		g.setColor(c);
	}
	
	public boolean isState() {
		return state;
	}

	public void setState(boolean state) {
		this.state = state;
	}

	public boolean isCanFire() {
		return canFire;	
	}

	public void setCanFire(boolean fire) {
		canFire = fire;
	}

	public boolean isCanPlace() {
		return canPlace;
	}

	public void setCanPlace(boolean canPlace) {
		this.canPlace = canPlace;
	}

	public void initial() {
		mBall = new Ball();
		balls.add(mBall);
		for ( int i = 0; i < 10; i++ )
			balls.add(new Ball());
	}

	public void gameStart() {
		
		double rad = Math.sqrt(3.0) * 10.0;
		double ix = 450, iy = 150;
		int    index = 0;
		balls.get(0).setProperty(150, 150, Color.WHITE);
		balls.get(0).setLive(true);
		for ( int i = 0; i < 4; i++ ) {
			for ( int j = 0; j <= i; j++ ) {
				balls.get(++index).setProperty(ix + i * rad, iy + (j - i / 2.0) * 20, Color.RED);
				balls.get(index).setLive(true);
			}
		}
		setCanFire(true);
		setCanPlace(true);
		parent.initScore();
		synchronized(this) {
			flashs.clear();
		}

	}

	public void addFlash(Tnode item) {
		synchronized(this) {
			flashs.add(item);
		}		
	}

	public void move() {  //  主要负责运动的处理.
		// 把一个单位, 划分为很小时间片, 然后进行模拟. 碰撞, 就是这样的.

		int ctype = 0;
		int step = 0, record = 0, mstep;
		int number = 0, slice, request; //balls.size();
		Ball sb1 = null, sb2 = null;
		while ( step < SLICE ) {  // time limited
			mstep = SLICE - step;
			ctype = CWNULL;
			sb1 = sb2 = null;
			for ( int i = 0; i < balls.size(); i++ ) {
				Ball b1 = balls.get(i); 
				if ( b1.isLive() == false ) continue;
				slice = b1.collidesHole(TABLE_WIDTH - 1, TABLE_HEIGHT - 1);
				if ( mstep > slice ) {
					mstep = slice;
					ctype = CWHOLE;
					sb1 = b1;
				}
				for ( int j = i + 1; j < balls.size(); j++ ) {	
					Ball b2 = balls.get(j);
					if ( b2.isLive() == false ) continue;
					slice = b1.collides(b2);
					if ( mstep > slice ) {
						mstep = slice;
						ctype = CWBALL;   // 1 表示两球碰撞.
						sb1 = b1; sb2 = b2;					
					}
				}	
				slice = b1.collidesWall(TABLE_WIDTH - 1, TABLE_HEIGHT - 1);	
				if ( mstep > slice ) {
					mstep = slice;
					ctype = CWWALL;  // 2 表示球与墙壁发生碰撞.
					sb1 = b1;
				}
			}
			for ( int i = 0; i < balls.size(); i++ ) {
				if ( balls.get(i).isLive() == true )
					balls.get(i).update(mstep);	
			}

			if ( ctype == CWBALL ) {  
				sb1.changes(sb2);
				new Thread(new MusicThread("Collide.wav")).start();
			} else if ( ctype == CWWALL ) {
				sb1.changesWall(TABLE_WIDTH - 1, TABLE_HEIGHT - 1);
				new Thread(new MusicThread("CollideEdge.wav")).start();
			} else if ( ctype == CWHOLE ) {
				Point tp = sb1.changesHole(TABLE_WIDTH - 1, TABLE_HEIGHT - 1);
				addFlash(new Tnode(tp.x, tp.y));
				if ( sb1 != mBall ) {
					parent.addScore();
				}
				new Thread(new Mp3Encode("../sounds/Pocket.wav")).start();
			}	
			step += mstep;		
		}
		int counter = 0;		
		for ( int i = 0; i < balls.size(); i++ ) {
			if ( balls.get(i).isStayed() == true || balls.get(i).isLive() == false )
				counter++;
		}
		if ( counter == balls.size() ) {
			if ( isCanFire() == false )
			       	parent.initialPower();
			setCanFire(true);
			if ( balls.get(0).isLive() == false ) {
				balls.get(0).setData(150, 150, 0, 0); // bug still exsit. 可能存在重叠
				balls.get(0).setLive(true);
				setCanPlace(true);
			}
		}


	}

	public boolean judge() {
		Ball b1 = balls.get(0);
		for ( int i = 1; i < balls.size(); i++ ) {
			Ball b2 = balls.get(i);
			if ( b1.isTouched(b1.x, b1.y, b2.x, b2.y) == true )
				return false;
		}	
		if ( b1.y <= Ball.OFFSET + b1.radian || b1.y >= TABLE_HEIGHT - Ball.OFFSET - b1.radian )
			return false;
		return true;
	}

	private class MouseMotionMonitor extends MouseMotionAdapter {
		
		public void mouseDragged(MouseEvent e) {
			if ( isCanFire() == true ) {
				pTarget = e.getPoint();
			}
			if ( isCanPlace() == true ) {
				balls.get(0).x = 150;
				balls.get(0).y = e.getPoint().y;
			}
		}

		public void mouseMoved(MouseEvent e) {
			if ( isCanFire() == true ) {
				pTarget = e.getPoint();
			}
			if ( isCanPlace() == true ) {
				
				balls.get(0).x = 150;
				
				balls.get(0).y = e.getPoint().y;
			}
		}

	}

	private class MouseMonitor extends MouseAdapter {  // 这边涉及一个同步问题.
		public PowerChooser chooser = null;
		public void mousePressed(MouseEvent e) {	
			if ( e.getButton() == MouseEvent.BUTTON1 && isCanFire() == true && isCanPlace() == false ) {	
				chooser = new PowerChooser();
				chooser.start();
			}
		}
		
		public void mouseReleased(MouseEvent e) {
			if ( e.getButton() == MouseEvent.BUTTON1 && isCanFire() == true ) {
				if ( isCanPlace() == false ) {
					setCanFire(false);
					mBall.setData(parent.getPower(), e.getPoint());	
					chooser.setLive(false);
				} else if ( judge() == true ) {
					setCanPlace(false);
				}
			}
		}

	}
	
	private class Tnode {
		
		public int x, y;
		public int index = 0;
		public Tnode(int x, int y) {
			this.x = x; 
			this.y = y;
		}

		public void draw(Graphics g) { // 0 1 2 3 2 1 0
			BufferedImage image = null;
			if ( index <= 3 )
				image = ImageLoader.getHitedImage(index);			
			else 
				image = ImageLoader.getHitedImage(6 - index);
			g.drawImage(image, x - image.getWidth() / 2, y - image.getHeight() / 2, null);
			index++;
		}	
		
		public boolean isLive() {
			 return index <= 6;
		}


	}	

	private class PowerChooser extends Thread {
		public boolean live = true;
		public void setLive(boolean live) {
			this.live = live;
		}
		public void run() {
			parent.initialPower();
			while ( live == true ) {
				try {
					Thread.sleep(80);
					parent.updatePower();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}	
		}
	}


}

// 使用一个隐藏组件, 来回避JProcessBar 这个高级Swing组件的model问题;
class ProcessPanel extends JPanel {

	public static final int PROCESS_WIDTH  = 600;
	public static final int PROCESS_HEIGHT = 50;
	public JLabel lblScore = null;
	public JLabel lblPower = null;
	public JTextField tfScore = null;
	public JButton btnStart = null;

	private int prec = 0;
	private int changedUnit = 2;

	public ProcessPanel() {

		lblScore = new JLabel("得分");
		lblPower = new JLabel("力量条");
		tfScore = new JTextField("0", 5);
		tfScore.setEditable(false);
		btnStart = new JButton("开始");
		deploy();
	}

	public void deploy() {
		setSize(PROCESS_WIDTH, PROCESS_HEIGHT);		
		setLayout(null);
		lblScore.setBounds(50, 10, 60, 30);
		tfScore.setBounds(120, 10, 60, 30);
		btnStart.setBounds(200, 10, 60, 30);
		lblPower.setBounds(300, 10, 60, 30);		
		add(lblScore);
		add(lblPower);
		add(btnStart);
		add(tfScore);
	}

	public void paint(Graphics g) {
		super.paint(g);
		Color c = g.getColor();
		g.setColor(Color.PINK);
		g.drawRect(0, 0, getSize().width - 1, getSize().height - 1);	
		g.drawRect(360, 10, 200, 30);
		g.setColor(Color.LIGHT_GRAY);
		g.fillRect(360, 10, 200 * (prec - 10) / 90, 30);
		g.setColor(c);
	}
	
	public int getPower() {
		return prec;
	}

	public synchronized void initialPower() {
		prec = 10;
		changedUnit = 2;
	}

	public synchronized void updatePower() {
		if ( prec >= 100 ) {
			changedUnit = -2;
		} else if ( prec <= 2 ) {
			changedUnit = 2;
		}
		prec += changedUnit;
	}

	public void addStartActionListener(ActionListener act) {
		btnStart.addActionListener(act);
	}

	public void addScore() {
		int value = Integer.parseInt(tfScore.getText());
		tfScore.setText(new Integer(value + 1).toString());
	}
 
	public void initScore() {
		tfScore.setText("0");
	}

}

// singleton pattern

class ImageLoader {
	
	public static final int NPICTURE = 18;
	public static final int NFLASH = 4;
	public static BufferedImage[] images = new BufferedImage[NPICTURE];
	public static BufferedImage   cursor = null;
	public static BufferedImage   background = null;
	public static BufferedImage[]   flashs = new BufferedImage[NFLASH];

	public synchronized static BufferedImage getIndexOf(int index) {  // check index, judge whether index skip the range;
		if ( images[index] == null ) {
			URLClassLoader loader = (URLClassLoader)ImageLoader.class.getClassLoader();
			URL url = loader.findResource("images/" + index + ".png");
			try {
				images[index] = ImageIO.read(url);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return images[index];
	}

	public synchronized static BufferedImage getCursor() {
		if ( cursor == null ) {
			URLClassLoader loader = (URLClassLoader)ImageLoader.class.getClassLoader();
			URL url = loader.findResource("images/cursor.gif");
			try {
				cursor = ImageIO.read(url);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return cursor;
	}

	public synchronized static BufferedImage getBackground() {
		if ( background == null ) {
			URLClassLoader loader = (URLClassLoader)ImageLoader.class.getClassLoader();
			URL url = loader.findResource("images/table.png");
			try {
				background = ImageIO.read(url);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return background;
	}	

	public synchronized static BufferedImage getHitedImage(int index) {
		if ( flashs[index] == null ) {
			URLClassLoader loader = (URLClassLoader)ImageLoader.class.getClassLoader();
			URL url = loader.findResource("images/" + index + ".gif");
			try {
				flashs[index] = ImageIO.read(url);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return flashs[index];
	}

}

// 显然, 用AudioClip 也可以来播放wav格式的文件
// Applet 中定义的AudioClip: getAudioClip();

class MusicThread implements Runnable {
	
	public AudioClip clip = null;
	public String filename = null;

	public MusicThread(String filename) {
		this.filename = filename;
	}
	public void run() {
		try {
			URLClassLoader loader = (URLClassLoader)getClass().getClassLoader();
			URL url = loader.findResource("sounds/" + filename);
			clip = Applet.newAudioClip(url);  // newAudioClip(url) 与 getAudioClip() 的区别是,一个是静态另一个是成员函数.
			clip.play();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

//  (1) 函数mouseMoved(MouseEvent e) 和mouseDragged(MouseEvent e) 两者都监控鼠标的移动, 但是, 前者为无按键按下时,
//  而后者则是按下某个组件时进行跟踪.
//  在监视mouseDragged(MouseEvent e) {} 这个函数时, 要用到MouseMotionAdapter类, 
//  因为mouseAdapter() 中可能不发送mouseDragged事件, 这与具体的实现有关.


// Thread ---> java.lang.Thread;

// (1)先搭框架.
// (2)先完成, 球碰台球桌面, 的碰撞检测, 以及碰撞反应.
// (3)简单的两个球的碰撞, 以及多个球的碰撞.
//
// (4) 安置力量条: 
// (5) 对碰撞的时候, 添加背景音乐.

// 今天的任务: 完成
// 给球贴上图.(option: 制作球滚动的效果).

// 书写 碰撞的原理 + 实现碰撞的过程.
//
// (1) 球进洞, + 显示进球的洞, 制作动画效果  \/
// (2) 对力量条的选择, 应该更加主动.  \/
// (3) 白球进洞后, 位置的重新选择.    \/
// (4) 开始, 即重新开始              \/
